package com.iflyings.android.dataconfig

import android.content.ContentValues
import android.content.Context
import android.net.Uri
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

abstract class DBLoaderImpl<T, R>(private val defaultValue: T,
                                  private val modelClass: Class<T>): ReadWriteProperty<R, T> {

    override fun getValue(thisRef: R, property: KProperty<*>): T {
        return TypeUtils.toType(getString(thisRef, property.name), defaultValue, modelClass)
    }

    override fun setValue(thisRef: R, property: KProperty<*>, value: T) {
        setString(thisRef, property.name, TypeUtils.toString(value))
    }

    protected abstract fun getString(thisRef: R, name: String): String?
    protected abstract fun setString(thisRef: R, name: String, value: String)

}

class DBLLoaderImpl<T>(defaultValue: T, modelClass: Class<T>):
    DBLoaderImpl<T, Any?>(defaultValue, modelClass) {

    override fun getString(thisRef: Any?, name: String) =
        IniSection.getValue(ConfigContentProvider.TABLE_NAME, name)
    override fun setString(thisRef: Any?, name: String, value: String) =
        IniSection.setValue(ConfigContentProvider.TABLE_NAME, name, value)

}

class DBRLoaderImpl<T>(defaultValue: T, modelClass: Class<T>):
    DBLoaderImpl<T, Context>(defaultValue, modelClass) {

    override fun getString(thisRef: Context, name: String): String? {
        val uri = Uri.withAppendedPath(ConfigContentProvider.getNotifyUri(ConfigContentProvider.TABLE_NAME), name)
        thisRef.contentResolver.query(uri, null, null, null, null)?.use {
            if (it.moveToNext()) {
                val valueIndex = it.getColumnIndex(IniSection.VALUE)
                if (valueIndex >= 0) {
                    return it.getString(valueIndex)
                }
            }
        }
        return null

    }
    override fun setString(thisRef: Context, name: String, value: String) {
        val uri = Uri.withAppendedPath(ConfigContentProvider.getNotifyUri(ConfigContentProvider.TABLE_NAME), name)
        thisRef.contentResolver.insert(uri, ContentValues().apply {
            put(IniSection.NAME, name)
            put(IniSection.VALUE, value)
        }) ?: throw IllegalArgumentException("can not insert $name:$value in $uri")
    }
}

//class DBLoader<T>(private val context: Context? = null,
//                  private val name: String,
//                  private val defaultValue: T,
//                  private val modelClass: Class<T>) {
//    operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadWriteProperty<Any?, T> {
//        checkProperty()
//        return if (context != null) {
//            DBRLoaderImpl(context, name, defaultValue, modelClass)
//        } else {
//            if (thisRef is Context) {
//                DBRLoaderImpl(thisRef, name, defaultValue, modelClass)
//            } else {
//                DBLLoaderImpl(name, defaultValue, modelClass)
//            }
//        }
//    }
//
//    private fun checkProperty() {
//        when (defaultValue) {
//            is Boolean, is Int, is Long, is Float, is Double, is String -> return
//        }
//        if (modelClass.isEnum) {
//            return
//        }
//        try {
//            modelClass.constructors.first {
//                return@first (it.parameterTypes.size == 1 && it.parameterTypes[0] == String::class.java)
//            }
//        } catch (e: NoSuchElementException) {
//            throw UnsupportedOperationException("${modelClass.canonicalName} don't has constructor function of String")
//        }
//    }
//}